package dbCore

import (
	"encoding/binary"
	"errors"
	"hash/crc32"
)

//KeySize, ValueSize, ExtraSize, crc32 均为 uint32 类型，各占 4 字节
//Type 和 Mark 占 2 + 2
//4 + 4 + 4 + 4 + 2 + 2 = 20
const entryHeaderSize = 20

type (
	Entry struct {
		Meta       *Meta  // Entry中存放的真实数据
		TypeOfData uint16 // 数据类型
		OpType     uint16 // 操作类型 删除、添加...
		crc32      uint32 // 校验和
	}

	Meta struct {
		Key       []byte
		Value     []byte
		Extra     []byte // 额外信息
		KeySize   uint32
		ValueSize uint32
		ExtraSize uint32
	}
)

// 新建一个entry
func NewEntry(key, value, extra []byte, typeOfData, opType uint16) *Entry {
	return &Entry{
		Meta: &Meta{
			Key:       key,
			Value:     value,
			Extra:     extra,
			KeySize:   uint32(len(key)),
			ValueSize: uint32(len(value)),
			ExtraSize: uint32(len(extra)),
		},
		TypeOfData: typeOfData,
		OpType:     opType,
	}
}

// 返回该entry的大小
func (entry *Entry) Size() uint32 {
	return entryHeaderSize + entry.Meta.KeySize + entry.Meta.ValueSize + entry.Meta.ExtraSize
}

// 对entry编码
func (entry *Entry) Encode() ([]byte, error) {
	if entry == nil || entry.Meta.KeySize == 0 {
		return nil, errors.New("Entry为空或者Entry的key为0")
	}

	ks, vs := entry.Meta.KeySize, entry.Meta.ValueSize
	es := entry.Meta.ExtraSize
	buf := make([]byte, entry.Size())

	binary.BigEndian.PutUint32(buf[4:8], ks)
	binary.BigEndian.PutUint32(buf[8:12], vs)
	binary.BigEndian.PutUint32(buf[12:16], es)
	binary.BigEndian.PutUint16(buf[16:18], entry.TypeOfData)
	binary.BigEndian.PutUint16(buf[18:20], entry.OpType)
	copy(buf[entryHeaderSize:entryHeaderSize+ks], entry.Meta.Key)
	copy(buf[entryHeaderSize+ks:(entryHeaderSize+ks+vs)], entry.Meta.Value)
	if es > 0 {
		copy(buf[(entryHeaderSize+ks+vs):(entryHeaderSize+ks+vs+es)], entry.Meta.Extra)
	}

	crc := crc32.ChecksumIEEE(entry.Meta.Value)
	binary.BigEndian.PutUint32(buf[0:4], crc)

	return buf, nil
}
